﻿package com.cilico.single.components;

import java.util.Vector;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;

import com.cilico.single.ConfigManager;
import com.cilico.single.Mid;
import com.cilico.single.R;
import com.cilico.single.utils.Utils;
import com.cilico.single.view.BaseView;

public class WaitDialog {
	public final static int TIMEOUT = 200; // 毫秒。更新WaitDialog的时间间隔
	protected final Mid mid_;
	private TextHandler handler_;
	protected int waiterTimeout_;
	private String text_;
	private int processingCtr_;
	Checker checker_;
	private int running_;
	private final Vector<Task> queue_ = new Vector<Task>();
	private boolean stopped_ = false;
	private boolean checkerStopped_ = false;
	private Task displayTask_;
	boolean isNoCancel_ = false; // true时等待对话框没有取消按钮

	public WaitDialog(Mid mid) {
		mid_ = mid;
		waiterTimeout_ = TIMEOUT;
		processingCtr_ = 0;
		running_ = 0;
		handler_ = new TextHandler();
		initWorkQueue();
	}

	void notifyHttpChecker() {
		synchronized (this) {
			if (checker_ == null || !checker_.isAlive()) {
				checker_ = new Checker(this);
				checker_.start();
			} else {
				this.notify();
			}
		}
	}

	/**
	 * Called from the checker thread when task finishes.
	 */
	private void done(Task task) {
		// 取消进度条显示
		// WaitDialog.Task.cancelProgressBar();
		BaseView.mid_.waitDialog_.setText(ConfigManager.currentView_.getString(R.string.waitmessage));
		displayTask_.totalSize_ = 0;
		handler_.onUpdate(this);
		if (task.stop_)
			return;
		if (task.hasFailed()) {
			task.onFailure(this);
		} else {
			task.onSuccess(this);
		}
		// NOTE: don't set checker_ or waiter_ to null here. We may erase the jobs that the next wait dialog has started.
	}

	/**
	 * Default handler with text message display.
	 */
	protected static class TextHandler {
		protected int ctr_;

		public TextHandler() {
			ctr_ = 0;
		}

		public void onUpdate(WaitDialog dlg) {
			// Since dialog's text may change, we can't use str_ here.
			final WaitDialog dlgTemp = dlg;
			dlgTemp.setProcessingCtr(ctr_);
			if (ctr_++ > 5) {
				ctr_ = 0;
			}
			ConfigManager.currentView_.runOnUiThread(new Runnable() {

				public void run() {
					// TODO Auto-generated method stub
					if (null != ConfigManager.currentView_.progressdialog_)
						ConfigManager.currentView_.progressdialog_.setMessage(dlgTemp.getLoadingStatus());
				}
			});
		}
	}

	String getLoadingStatus() {
		if (displayTask_.totalSize_ == 0 || displayTask_.totalSize_ == -1) {
			return text_;
		} else {
			String status;
			int progress;
			try {
				// 该try操作为防止除数为零的情况出现，由于线程问题有可能会出现除数为零的问题
				progress = 100 * displayTask_.finishSize_ / displayTask_.totalSize_;
			} catch (Exception e) {
				return text_;
			}
			StringBuffer buf = new StringBuffer();
			buf.append(text_);
			// buf.append(": ");
			buf.append(progress);
			buf.append("%");
			status = buf.toString();
			return status;
		}
	}

	private void initWorkQueue() {
		int num = Utils.NUM_THREADS;
		if (num > 1) {
			num--;
			new Worker(this, 0).start();// This worker only for task.priority = 0.
		}
		for (int i = 0; i < num; i++) {
			new Worker(this, 100000).start();
		}
	}

	private static class Worker extends Thread {
		private final WaitDialog dlg_;
		private int prio_;// This thread only deal with the task who's priority not bigger than prio_.
		Worker(WaitDialog dlg, int priority) {
			super();
			dlg_ = dlg;
			prio_ = priority;
		}

		public void run() {
			Task task;
			for (;;) {
				try {
					sleep(50);
				} catch (InterruptedException ex) {
				}
				// Get a task from the queue
				synchronized (dlg_.queue_) {
					try {
						while (!dlg_.stopped_ && dlg_.queue_.isEmpty())
							dlg_.queue_.wait();
					} catch (InterruptedException e) {
						// Ignore interrupts while we were idle. If someone
						// really wants us to stop, she must call stop()!
					}
					if (dlg_.stopped_)
						return;
					if (dlg_.queue_.isEmpty()) {
						// We must have got interrupted from the wait() loop
						// while having no jobs at all. Go back to wait.
						continue;
					} else {
						task = (Task) dlg_.queue_.firstElement();
						// only deal with who's priority not bigger than prio_
						if (task.priority_ > prio_) {
							// notify other thread which wait for this. if not will lead to dead lock.
							dlg_.queue_.notifyAll();
							continue;
						}
						dlg_.queue_.removeElementAt(0);
						if (task.stop_)
							continue;
					}
				}
				synchronized (dlg_) {
					++dlg_.running_;
				}
				boolean done = false;
				try {
					dlg_.notifyHttpChecker();
					task.mainEntrance(dlg_);
					// The cleanup of a task will run from the main thread.
					final Task t = task;
					// 原项目该操作为另起线程
					new Thread() {
						public void run() {
							dlg_.done(t);
						}
					}.start();
					done = true;
				} finally {
					synchronized (dlg_) {
						--dlg_.running_;
					}
					if (!done) {
						// should not throw any exception, but may be
						// interrupted by runtime or other errors. When that happens, we
						// detect that by the done flag, and let this thread terminate, and start a
						// new one to replace.
						new Worker(dlg_, prio_).start();
						break;
					}
				}
			}
		}
	}

	/**
	 * Instance inner class to periodically check waiter thread status and update UI if available.
	 */
	private class Checker extends Thread {
		private final WaitDialog dlg_;
		public Checker(WaitDialog dlg) {
			super();
			dlg_ = dlg;
		}

		public void run() {
//			boolean empty = false;
			while (true) {
				try {
					sleep(waiterTimeout_);
				} catch (InterruptedException ex) {
					// Ignore and continue to wait.
				}
				synchronized (dlg_) {
					try {
						while (dlg_.running_ <= 0 && !dlg_.checkerStopped_)
							dlg_.wait();
					} catch (InterruptedException ignored) {
					}
					if (dlg_.checkerStopped_)
						return;
				}
				handler_.onUpdate(dlg_);
			}
		}
	}

	/**
	 * Modeled after CancellableTask, this class does not depends on Executable nor Runnable but offers similar
	 * functionality. It is intended to be extended as anonymous classes when calling startTask(), and caller should
	 * define run().
	 */
	public abstract static class Task {
		protected Exception exception_ = null;
		protected String errmsg_ = null;
		protected String name_;
		protected int priority_;
		private int totalSize_ = -1;
		private int finishSize_;
		public boolean stop_;

		public Task(int priority) {
			name_ = null;
			priority_ = priority;
			stop_ = false;
		}

		public Task(String name, int priority) {
			name_ = name;
			priority_ = priority;
			stop_ = false;
		}

		public void setTotalSize(int total) {
			totalSize_ = total;
			finishSize_ = 0;
		}

		public void updateFinish(int finish) {
			finishSize_ += finish;
		}

		public String getErrMsg() {
			if (errmsg_ == null && exception_ != null)
				return exception_.toString();
			else
				return errmsg_;
		}

		public Exception getException() {
			return exception_;
		}

		public boolean hasFailed() {
			return (exception_ != null || errmsg_ != null);
		}

		void setException(Exception e) {
			exception_ = e;
		}

		public void setErrMsg(String msg) {
			errmsg_ = msg;
		}

		public void setErrMsg(Exception e) {
			errmsg_ = (e == null) ? null : e.getMessage();
		}

		/**
		 * This is the main entrance point so that if any exception leaks out of run(), we'll capture it here and set
		 * error msg accordingly.
		 */
		void mainEntrance(WaitDialog dlg) {
			try {
				run(dlg);
			} catch (Exception ex) {
				Utils.printOutToConsole("ex:" + ex.toString());
				setErrMsg(ex);
				setException(ex);
				// cancelProgressBar(ConfigManager.currentView_);
			} catch (Throwable ex) {
				// In case something implementation-specific leaked out.
				setErrMsg(ex.getMessage());
				// cancelProgressBar(ConfigManager.currentView_);
			}
		}

		// 取消已存在的进度条显示
		public static void cancelProgressBar(BaseView activity) {
			if (null != activity.progressdialog_) {
				if (activity.progressdialog_.isShowing()) {
					if (!activity.isFinishing()) {
						activity.progressdialog_.dismiss();
						activity.progressdialog_ = null;
					}
				}
			}
		}

		/**
		 * We do not use Runnable::run() so that we can throw exceptions. We had to throw the very basic exception
		 * because we don't know what are out there.
		 */
		abstract public void run(WaitDialog dlg) throws Exception;

		public void onSuccess(WaitDialog dlg) {
			// 取消进度条显示
			// WaitDialog.Task.cancelProgressBar(ConfigManager.currentView_);
		}

		public void onFailure(WaitDialog dlg) {
			// 取消进度条显示
			WaitDialog.Task.cancelProgressBar(ConfigManager.currentView_);
		}

		/**
		 * @param stop_ the stop_ to set
		 */
		public void setStop(boolean stop_) {
			this.stop_ = stop_;
		}

		/**
		 * @return the stop_
		 */
		public boolean isStop() {
			return stop_;
		}
	}

	/**
	 * Display a progress indicator.
	 */
	public final void drawProcessing(Canvas g, int centerX, int centerY) {
		Paint paint = new Paint();
		g.clipRect(centerX, centerY, centerX + 50, centerY + 5);
		g.drawBitmap(Utils.progress_, centerX, centerY, paint);
		g.clipRect(new Rect(centerX + processingCtr_ * 9, centerY, centerX + processingCtr_ * 9 + 50 - processingCtr_ * 9, centerY + 5));
		g.drawBitmap(Utils.progress_, centerX + processingCtr_ * 9, centerY, paint);
		g.clipRect(centerX, centerY, centerX + 50, centerY + 5);
		g.drawBitmap(Utils.progress_, centerX - (54 - processingCtr_ * 9), centerY, paint);
		g.clipRect(0, 0, Utils.screenWidth_, Utils.screenHeight_);
	}

	void setProcessingCtr(int i) {
		processingCtr_ = i;
	}

	public boolean removeTask(String name) {
		synchronized (queue_) {
			for (int i = 0; i < queue_.size(); i++) {
				Task task = (Task) queue_.elementAt(i);
				if (task.name_ != null && task.name_.equals(name)) {
					queue_.removeElementAt(i);
					return true;
				}
			}
			return false;
		}
	}

	public void setText(String t) {
		text_ = t;
	}

	/**
	 * Adds a background task who's run() method will be invoked asynchronously by a worker thread.
	 */
	public void addBgTask(Task task) {
		// 在该处重新赋值改变量
		displayTask_ = task;

		if (task == null) {
			return;
		}
		synchronized (queue_) {
			if (stopped_) {
				return;
			}
			boolean flag = true;
			for (int i = queue_.size() - 1; i >= 0; i--) {
				Task temp = (Task) queue_.elementAt(i);
				if (task.priority_ >= temp.priority_) {
					queue_.insertElementAt(task, i + 1);
					flag = false;
					break;
				}
			}
			if (flag)
				queue_.insertElementAt(task, 0);
			queue_.notify();
		}
	}

	/**
	 * Indicate that the wait dialog don't have cancel action when it is true. This wait dialog is a full screen dialog.
	 */
	public void addFgTask(BaseView bv, Task task, boolean isPartScreen, boolean isNoCancel) {
		addFgTask(bv, task, isPartScreen);
		isNoCancel_ = isNoCancel;
	}

	/**
	 * Adds a foreground task who's run() method will be invoked asynchronously by a worker thread.
	 */
	public void addFgTask(final BaseView activity, Task task, boolean isPartScreen) {
		isNoCancel_ = false; // init to false everytime.
		displayTask_ = task;
		displayTask_.totalSize_ = -1;
		displayTask_.finishSize_ = 0;
		if (!activity.isFinishing()) {
			activity.runOnUiThread(new Runnable() {
				public void run() {
					WaitDialog.Task.cancelProgressBar(activity);
					activity.onCreateProgressDialog(activity, displayTask_, getLoadingStatus());
				}
			});
		}
//		firstPaint_ = true;
		addBgTask(task);
	}

	/**
	 * 该方法指示单纯的显示进度条
	 * @param activity 进度条被加载的界面
	 */
	public void showProgressdialog(BaseView activity) {
		WaitDialog.Task.cancelProgressBar(activity);
		if (!activity.isFinishing()) {
			activity.onCreateProgressDialog(activity, null, getLoadingStatus());
		}
	}

	public void cancelAndRemoveTask(String taskName) {
		displayTask_.stop_ = true;
		Utils.LogE("debug", "displayTask_.stop_ = " + displayTask_.stop_);
		removeTask(taskName);
		// mid_.channelEntity_.cleanClonePoiRepository();
	}
	
	public boolean getDisplayTaskStatus () {
		Utils.LogE("debug", "get displayTask_.stop_ = " + displayTask_.stop_);
		return displayTask_.stop_;
	}
	
	public void setDisplayTaskStatus (Boolean bl) {
		displayTask_.stop_ = bl;
	}
}
